// 基于阻塞队列的生产者消费者模型
#pragma once
#include <iostream>
#include <queue>
#include <unistd.h>
#include <pthread.h>
#include "LockGroud.hpp"
using namespace std;

const int DEFAULT = 5; // 队列默认容量大小

template <class T>
class BlockQueue
{
public:
    BlockQueue(int capacity = DEFAULT)
        : _capacity(capacity)
    {
        pthread_mutex_init(&_mtx, nullptr);  // 初始化互斥量
        pthread_cond_init(&_full, nullptr);  // 初始化条件变量
        pthread_cond_init(&_empty, nullptr); // 初始化条件变量
    }
    void push(T &in)
    {
        // // 1. 访问临界资源保证线程安全要加锁 这里的阻塞队列就是临界资源
        // pthread_mutex_lock(&_mtx);
        // // 2. 检查临界资源是否准备完成(如果当前阻塞队列满了,生产者线程就不想继续向队列中push数据。就在满的条件变量下进行等待)
        // while (IsFull())//必须使用while进行判断
        // {
        //     pthread_cond_wait(&_full, &_mtx);
        // }
        // _bqueue.push(in);
        // pthread_mutex_unlock(&_mtx);
        // pthread_cond_signal(&_empty);

        // RAII机制的加锁方式
        LockGroud lockgroud(&_mtx);
        while (IsFull()) // 必须使用while进行判断
        {
            pthread_cond_wait(&_full, &_mtx);
        }
        _bqueue.push(in);
        pthread_cond_signal(&_empty);
        // 函数调用结束，lockgrou局部对象生命周期结束自动调用析构函数进行解锁
    }
    void pop(T *out)
    {
        // // 1. 访问临界资源保证线程安全要加锁 这里的阻塞队列就是临界资源
        // pthread_mutex_lock(&_mtx);
        // // 2. 检查临界资源是否准备完成(如果当前阻塞队列为空,消费者线程就不能继续从队列中pop数据。在空的条件变量下进行等待)
        // while (IsEmpty()) // 必须使用while进行判断
        // {
        //     pthread_cond_wait(&_empty, &_mtx);
        // }
        // *out = _bqueue.front();
        // _bqueue.pop();
        // pthread_mutex_unlock(&_mtx);
        // pthread_cond_signal(&_full);

        // RAII机制的加锁方式
        LockGroud lockgroud(&_mtx);
        while (IsEmpty()) // 必须使用while进行判断
        {
            pthread_cond_wait(&_empty, &_mtx);
        }
        *out = _bqueue.front();
        _bqueue.pop();
         // 函数调用结束，lockgrou局部对象生命周期结束自动调用析构函数进行
    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mtx);  // 销毁互斥量
        pthread_cond_destroy(&_full);  // 销毁条件变量
        pthread_cond_destroy(&_empty); // 销毁条件变量
    }

private:
    //  检查当前阻塞队列是否为满
    bool IsFull()
    {
        return _bqueue.size() == _capacity;
    }
    // 检查当前阻塞队列是否为空
    bool IsEmpty()
    {
        return _bqueue.size() == 0;
    }

private:
    queue<T> _bqueue;      // 阻塞队列
    size_t _capacity;      // 队列容量
    pthread_mutex_t _mtx;  // 互斥量
    pthread_cond_t _full;  // 判满的条件变量
    pthread_cond_t _empty; // 判空的条件变量
};
